﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace OnCommon
{
    public class SocketTcpClient : IDisposable
    {
        internal Socket _Socket;
        internal SocketTcpServer _servrer;
        List<SocketTcpData> objs;//dic<threadId,queue<SocketTcpData>>
        AutoResetEvent resetEv = new AutoResetEvent(false);
        public event Action<SocketTcpFileData, bool> OnFileCompleted;


        public void Connect(string host, int port)
        {
            if (_Socket == null)
            {
                _Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                _Socket.Connect(host, port);
            }
            if (_servrer == null)
            {
                _servrer = new SocketTcpServer();
                _servrer.OnCompleted += _servrer_OnCompleted;
                _servrer.OnFileCompleted += _servrer_OnFileCompleted;
               
                var receiveArgEx = _servrer.GetSocketAsyncEventEx(_Socket);
                _Socket.ReceiveAsync(receiveArgEx);
            }
        }

        public void Send(string msg)
        {

            byte[] sendbuffers = Encoding.UTF8.GetBytes(msg);
            Packet packet = new Packet();
            packet.PacketType = PacketType.String;
            packet.Length = sendbuffers.Length;
            this.SendInternal(packet, sendbuffers);
        }

        public void Send(object obj)
        {
            var msg = Newtonsoft.Json.JsonConvert.SerializeObject(obj);
            byte[] sendbuffers = Encoding.UTF8.GetBytes(msg);
            Packet packet = new Packet();
            packet.PacketType = PacketType.Complex;
            packet.Length = sendbuffers.Length;
            this.SendInternal(packet, sendbuffers);
        }

        public void Send(byte[] bytes)
        {
            Packet packet = new Packet();
            packet.PacketType = PacketType.Binary;
            packet.Length = bytes.Length;
            this.SendInternal(packet, bytes);
        }

        public void Send(FileStream fs, string fileName = null, int buffLen = 1024)
        {
            if (buffLen < 1024)
                buffLen = 1024;
            if (String.IsNullOrEmpty(fileName))
                fileName = Path.GetFileName(fs.Name);



            //文件信息结构
            //8byte 附加信息长度 + 附加信息 + file

            var fileNameByte = Encoding.UTF8.GetBytes(fileName);
            var filePacket = new byte[8 + fileNameByte.Length];

            var lByte = Encoding.UTF8.GetBytes(fileNameByte.Length.ToString());
            Array.Copy(lByte, 0, filePacket, 8 - lByte.Length, lByte.Length);
            Array.Copy(fileNameByte, 0, filePacket, 8, fileNameByte.Length);


            Packet packet = new Packet();
            packet.PacketType = PacketType.File;
            packet.Length = (int)fs.Length + filePacket.Length;

            this.SendInternal(packet, new byte[] { });//发送包头


            this._Socket.Send(filePacket);//发送文件附加信息
            //发送文件字节
            var bytes = new byte[buffLen];
            int readLen = 0;
            do
            {
                readLen = fs.Read(bytes, 0, buffLen);
                if (readLen > 0)
                {
                    if (readLen == buffLen)
                    {
                        this._Socket.Send(bytes);
                    }
                    else
                    {
                        var bytes2 = new byte[readLen];
                        Array.Copy(bytes, 0, bytes2, 0, readLen);
                        this._Socket.Send(bytes2);
                    }
                }
            } while (readLen > 0);

        }

        private void SendInternal(Packet packet, byte[] bytes)
        {

            var lByte = Encoding.UTF8.GetBytes(packet.Length.ToString());


            var msgByte = new byte[9 + bytes.Length];
            msgByte[0] = Encoding.UTF8.GetBytes(((int)packet.PacketType).ToString())[0];


            Array.Copy(lByte, 0, msgByte, 9 - lByte.Length, lByte.Length);
            Array.Copy(bytes, 0, msgByte, 9, bytes.Length);




            this._Socket.Send(msgByte);



        }

        public SocketTcpData Receive()
        {
            if (this.objs == null || this.objs.Count == 0)
                resetEv.WaitOne(2000);
            if (this.objs == null || this.objs.Count == 0)
                return null;
            var _r = this.objs.First();

            lock (this.objs)
            {
                this.objs.RemoveAt(0);
            }

            return _r;
        }

        void _servrer_OnCompleted(SocketTcpData obj)
        {
            if (this.objs == null)
                this.objs = new List<SocketTcpData>();
            lock (this.objs)
            {
                this.objs.Add(obj);
            }
            resetEv.Set();
        }

        void _servrer_OnFileCompleted(SocketTcpFileData arg1, bool arg2)
        {
            if (OnFileCompleted != null)
                OnFileCompleted(arg1, arg2);
        }

        public void Dispose()
        {
            if (_Socket != null)
                _Socket.Dispose();
            _Socket = null;

            if (_servrer != null)
                _servrer.Dispose();

            resetEv.Dispose();
        }

    }
}
